using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Rendering;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace IQIGame.Onigao.Game
{
    public partial class UIManager : FrameMonoModule
    {
        //所有打开的UI窗体(包含显示和正在加载中的)
        private Dictionary<Type, UIBaseWindow> _allWindows = new();
        private List<UIBaseWindow> _allList = new();
        //所有打开的UI窗体，按层级分(包含显示和正在加载中的)
        public List<UIBaseWindow>[] _allLayerWindows;

        // UI显示緩存
        private CacheUIHelper cacheUIHelper;

        //异步加载中的UI窗体
        private HashSet<Type> _asyncLoading = new();

        private bool isFullScreenShowing = false;

        #region internal
        private void AddAsyncLoading(Type type)
        {
            //异步加载中的
            _asyncLoading.Add(type);
            //添加遮罩
            ChangeLockMaskState(true);
        }

        private void RemoveAsyncLoading(Type type)
        {
            _asyncLoading.Remove(type);
            if (_asyncLoading.Count == 0)
            {
                //删除遮罩
                ChangeLockMaskState(false);
            }
        }

        private void ClearAllAsyncLoading()
        {
            _asyncLoading.Clear();
            //删除遮罩
            ChangeLockMaskState(false);
        }

        private const int lmtMaxSort = (short.MaxValue / 100 - 10) * 100;
        private (int, int) GetWindowLastSortOrder(EUILayer uiLayer)
        {
            var layerWindows = _allLayerWindows[(int)uiLayer];
            int lastOrder = 0;
            int sortOrder = 0;
            bool isHasWindow = layerWindows.Count > 0;
            if (isHasWindow)
                lastOrder = layerWindows[layerWindows.Count - 1].winSetting.sortOrder;

            if (lastOrder >= lmtMaxSort)
            {
                lastOrder = -1 * UIConstant.SortOrderBetweenUI;
                UIBaseWindow _mUpObj = null;
                for (int i = 0; i < layerWindows.Count; i++)
                {
                    lastOrder += UIConstant.SortOrderBetweenUI;
                    _mUpObj = layerWindows[i];
                    _mUpObj.ResetWinSettingSortOrder(lastOrder);
                }
            }
            if (isHasWindow)
                sortOrder = lastOrder + UIConstant.SortOrderBetweenUI;
            return (sortOrder, lastOrder);
        }

        private int GetWindowNewSortOrder(EUILayer uiLayer)
        {
            var orderVal = GetWindowLastSortOrder(uiLayer);
            return orderVal.Item1;
        }

        private UIBaseWindow NewWindowObj(Type type)
        {
            return Activator.CreateInstance(type) as UIBaseWindow;
        }

        private UIBaseWindow GetWindow(Type type)
        {
            if (this._allWindows.TryGetValue(type, out var window))
                return window;
            return null;
        }

        private T GetWindow<T>() where T : UIBaseWindow
        {
            return GetWindow(typeof(T)) as T;
        }

        private void AddUIUseCache(UIBaseWindow window)
        {
            if (window == null)
                return;

            var winType = window.GetType();
            if (this._allWindows.ContainsKey(winType))
                return;

            this._allList.Add(window);
            this._allWindows.Add(winType, window);

            int layer = (int)window.winSetting.layer;
            var layerWindows = _allLayerWindows[layer];
            layerWindows.Add(window);
        }

        private void UpUIUseCache(UIBaseWindow window)
        {
            if (window == null)
                return;

            var winType = window.GetType();
            if (!this._allWindows.ContainsKey(winType))
                return;

            this._allList.Remove(window);
            this._allList.Add(window);

            int layer = (int)window.winSetting.layer;
            var layerWindows = _allLayerWindows[layer];
            layerWindows.Remove(window);

            if (layerWindows.Count > 0)
            {
                var lastOrder = layerWindows[layerWindows.Count - 1].winSetting.sortOrder;
                var currOrder = lastOrder + UIConstant.SortOrderBetweenUI;
                window.ResetWinSettingSortOrder(currOrder);
            }

            layerWindows.Add(window);
        }

        /// <summary>
        /// 移除并销毁 window
        /// </summary>
        private bool RemoveAndDispose(UIBaseWindow window)
        {
            if (window == null || !this._allWindows.ContainsKey(window.GetType()))
                return false;
            this._allWindows.Remove(window.GetType());
            this._allList.Remove(window);
            int layer = (int)window.winSetting.layer;
            var layerWindows = _allLayerWindows[layer];
            layerWindows.Remove(window);

            window.Dispose();
            return true;
        }

        private void InitWindowBaseInfo(UIBaseWindow window, ref IUICreateConfig setting, out string realPath, out ResLoader resloader)
        {
            if (setting == null)
            {
                setting = window.createConfig;
            }
            if (setting == null)
            {
                realPath = null;
                resloader = null;
                LogGame.LogError($"UI Window Create Setting is null:{window.GetType().FullName}");
                return;
            }
            resloader = ResLoader.Create();
            realPath = setting.Init().prefabPath;

            int sortOrder = GetWindowNewSortOrder(setting.layer);
            window.InitWinSettings(sortOrder, resloader, setting);
            this.AddUIUseCache(window);
        }

        private void InitWindowAfterLoaded(UIBaseWindow window, GameObject prefab, IUICreateConfig setting)
        {
            GameObject go = Instantiate(prefab, GetUILayoutLevelRoot(setting.layer));
            go.name = prefab.name;
            var rectTransform = go.transform as RectTransform;
            rectTransform.ResetRectTransform();
            //初始化
            go.SetActive(false);
            window.Init(go);
        }
        #endregion

        #region public window function
        public bool IsWindowShowing<T>() where T : UIBaseWindow
        {
            var window = GetWindow<T>();
            return window != null && window.isActive;
        }

        public bool IsMainWindowTopShowing()
        {
            UIBaseWindow topWindow = GetTopShowingWindow();
            return topWindow != null && topWindow.winSetting.cfgRuntime.layer == EUILayer.Main;
        }

        private UIBaseWindow GetTopShowingWindow()
        {
            if (_allList.Count <= 0)
                return null;
            for (int i = _allList.Count - 1; i >= 0; i--)
            {
                var window = _allList[i];
                if (window.isActive)
                {
                    return window;
                }
            }
            return null;
        }

        public bool IsTopShowing<T>() where T : UIBaseWindow
        {
            UIBaseWindow topWindow = GetTopShowingWindow();
            return typeof(T).IsInstanceOfType(topWindow);
        }

        public T GetEntityUI<T>() where T : UIEntityWindow
        {
            return this.GetWindow<T>();
        }

        private UIBaseWindow CreateWindow(Type windowType, IUICreateConfig setting = null)
        {
            var window = this.NewWindowObj(windowType);
            this.InitWindowBaseInfo(window, ref setting, out var preafbPath, out var resloader);

            if (setting.activeBlurBg && setting.blurDownSampling > 0)
            {
                LogGame.LogWarning("同步加载不支持模糊背景!");
            }
            var prefab = resloader.LoadABAsset<GameObject>(preafbPath);
            if (prefab == null)
            {
                RemoveAndDispose(window);
                LogGame.LogError($"UIWindow load failed:{window.GetType().FullName}");
                return null;
            }
            InitWindowAfterLoaded(window, prefab, setting);
            return window;
        }

        private T CreateWindow<T>(IUICreateConfig setting = null) where T : UIBaseWindow, new()
        {
            return this.CreateWindow(typeof(T), setting) as T;
        }

        private async UniTask<UIBaseWindow> CreateWindowAsync(Type windowType, IUICreateConfig setting = null)
        {
            var window = this.NewWindowObj(windowType);
            this.InitWindowBaseInfo(window, ref setting, out var preafbPath, out var resloader);

            Type type = window.GetType();
            //异步加载中的
            AddAsyncLoading(type);
            var prefab = await resloader.LoadABAssetAwait<GameObject>(preafbPath);
            RemoveAsyncLoading(type);

            if (prefab == null)
            {
                RemoveAndDispose(window);
                LogGame.LogError($"UIWindow load failed:{window.GetType().FullName}");
                return null;
            }
            InitWindowAfterLoaded(window, prefab, setting);
            //添加模糊
            await window.TrtSetBGRawImageTexture();
            return window;
        }

        private async UniTask InnerShowWindow(Type windowType, UIBaseData data = null, IUICreateConfig setting = null)
        {
            UIBaseWindow result = this.GetWindow(windowType);
            if (result != null && result.gameObject == null)
            {
                LogGame.LogError("异步打开UI出错：window已打开但却没有gameObject，怀疑异步方法在没有await的情况下被连续调用了多次。");
                return;
            }
            if (result != null)
            {
                this.UpUIUseCache(result);
            }
            else
            {
                result = await CreateWindowAsync(windowType, setting);
            }

            if (result == null)
                return;

            this.cacheUIHelper.InitShowStack(result);

            result.Show(data);
        }

        /// <summary>
        /// 创建并显示Window，异步可等待
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="setting"></param>
        /// <returns></returns>
        public async UniTask ShowWindowAsync<T>(UIBaseData data = null, IUICreateConfig setting = null) where T : UIBaseWindow, new()
        {
            await InnerShowWindow(typeof(T), data, setting);
        }

        /// <summary>
        /// 不需要等待的异步加载(流程上，自我斟酌是否需要等待)
        /// </summary>
        public async UniTask ShowWindowAsync(Type windowType, UIBaseData data = null, IUICreateConfig setting = null)
        {
            await InnerShowWindow(windowType, data, setting);
        }

        /// <summary>
        /// 恢复UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="setting"></param>
        /// <returns></returns>
        private async UniTask RecoverWindowAsync(Type windowType, UIBaseData data = null)
        {
            await InnerShowWindow(windowType, data, null);
        }

        /// <summary>
        /// 创建并显示Window
        /// </summary>
        public void ShowWindow<T>(UIBaseData data = null, IUICreateConfig setting = null) where T : UIBaseWindow, new()
        {
            this.InnerShowWindow(typeof(T), data, setting).Forget();
        }

        /// <summary>
        /// 同步打开UI
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="setting"></param>
        public void ShowWindowSync<T>(UIBaseData data = null, IUICreateConfig setting = null) where T : UIBaseWindow, new()
        {
            T result = GetWindow<T>();
            if (result != null)
            {
                this.UpUIUseCache(result);
            }
            else
            {
                result = CreateWindow<T>(setting);
            }

            this.cacheUIHelper.InitShowStack(result);

            result?.Show(data);
        }

        public void CloseWindow(UIBaseWindow window, bool isRecover)
        {
            if (!window.winSetting.permanent)
            {
                bool isSuccess = this.RemoveAndDispose(window);
                if (isSuccess)
                {
                    this.RemoveAsyncLoading(window.GetType());
                }
            }
            else
            {
                window.Hide();
            }
            this.cacheUIHelper.OnCloseUI(window, isRecover).Forget();
        }

        public void CloseWindow<T>(bool isRecover = true) where T : UIBaseWindow
        {
            Type type = typeof(T);
            UIBaseWindow window = this.GetWindow(type);
            if (window == null)
                return;
            this.CloseWindow(window, isRecover);
        }

        /// <summary>
        /// 关闭所有UI窗体
        /// </summary>
        /// <param name="containPermanent">是否包含常驻UI</param>
        public void CloseAllWindow(bool containPermanent)
        {
            //清除所有的异步加载中的UI
            this.ClearAllAsyncLoading();
            this.cacheUIHelper.Clear();

            var windows = ListPool<UIBaseWindow>.Get();
            windows.AddRange(this._allList);
            foreach (var window in windows)
            {
                if (containPermanent || !window.winSetting.permanent)
                {
                    this.RemoveAndDispose(window);
                }
            }
            ListPool<UIBaseWindow>.Put(windows);
        }
        #endregion

        private void InvokeKeyEvent()
        {
            if (UnityEngine.Input.GetKeyUp(KeyCode.Escape))
            {
                //响应返回键
                UIBaseWindow _mUpObj = null;
                for (int i = _allList.Count - 1; i >= 0; i--)
                {
                    _mUpObj = _allList[i];
                    if (!_mUpObj.isActive)
                        break;
                    if (_mUpObj.EscapeBack())
                        break;
                }
            }
        }

        private void OnUpdate4Windows(float deltaTime, float unscaledDeltaTime)
        {
            if (_allList.Count <= 0)
                return;
            UIBaseWindow _mUpObj = null;
            for (int i = _allList.Count - 1; i >= 0; i--)
            {
                _mUpObj = _allList[i];
                if (_mUpObj.isUpdate)
                    _mUpObj.Update(deltaTime, unscaledDeltaTime);
            }
        }

        private void OnLateUpdate4Windows()
        {
            if (_allList.Count <= 0)
                return;
            UIBaseWindow _mUpObj = null;
            for (int i = _allList.Count - 1; i >= 0; i--)
            {
                _mUpObj = _allList[i];
                if (_mUpObj.isLateUpdate)
                    _mUpObj.LateUpdate();
            }
        }

        private void OnLateUpdate4OptimizeFullScreen()
        {
            bool isFullScreenShowing = this.IsFullScreenShowing();
            if (this.isFullScreenShowing != isFullScreenShowing)
            {
                this.isFullScreenShowing = isFullScreenShowing;
                GameRenderLOD.Instance.OnFullScreenUIEnabled(isFullScreenShowing);
            }
        }

        private bool IsFullScreenShowing()
        {
            if (_allList.Count <= 0)
                return false;
            UIBaseWindow _mUpObj = null;
            for (int i = _allList.Count - 1; i >= 0; i--)
            {
                _mUpObj = _allList[i];
                if (_mUpObj.IsFullScreenAndActive())
                {
                    return true;
                }
            }
            return false;
        }
    }
}
